using BarManager.Core.ApplicationService.Interfaces;
using BarManager.Core.Entity;
using BarManager.UI.Web.Caching;
using BarManager.UI.Web.HtmlHelpers;
using BarManager.UI.Web.Models;
using BarManager.UI.Web.PrintService;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Web.Mvc;

namespace BarManager.UI.Web.Controllers
{
    [Authorize(Users = "admin")]
    public class OrdenController : BaseController<OrdenViewModel, Orden, long>
    {
        private IEmpleadoService EmpleadoService { get; set; }
        private IPromocionService PromocionService { get; set; }
        private ICategoriaService CategoriaService { get; set; }
        private IZonaService ZonaService { get; set; }
        private IMesaService MesaService { get; set; }
        private IProductoService ProductoService { get; set; }
        private IOrdenService OrdenService { get { return (IOrdenService)Service; } }

        public OrdenController(IOrdenService service, IEmpleadoService empleadoService,
            IPromocionService promocionService, ICategoriaService categoriaService,
            IZonaService zonaService, IMesaService mesaService, IProductoService productoService)
            : base(service)
        {
            this.EmpleadoService = empleadoService;
            this.PromocionService = promocionService;
            this.CategoriaService = categoriaService;
            this.ZonaService = zonaService;
            this.MesaService = mesaService;
            this.ProductoService = productoService;
        }

        public override ActionResult Index(long id = 0)
        {

            OrdenViewModel model = new OrdenViewModel
            {
                Empleados = EmpleadoService.GetAllEmpleadosAtiendeMesayActivos().OrderBy(x => x.NombreApellido),
                Promociones = PromocionService.GetAllActivas().OrderBy(x => x.Descripcion),
                Categorias = CategoriaService.GetAllCategoriasContainsProductos().OrderBy(x => x.Nombre)
            };

            if (id > 0)
            {
                model.Entity = OrdenService.Get(x => x.Id == id);
                CacheManager.Set("OrdenViewModel", model);
                CacheManager.Set("OrdenDetalleBase", model.Entity.Detalle.ToList<OrdenDetalleBase>());
                model.Zonas = ZonaService.GetAllZonasHabilitadasContainsMesa().OrderBy(x => x.Descripcion);
            }
            else
            {
                model.Zonas = ZonaService.GetAllZonasHabilitadasContainsMesasLibres().OrderBy(x => x.Descripcion);
            }
            return View(model);
        }

        [HttpGet]
        public ActionResult Cancel(long id = 0)
        {
            if (id > 0) OrdenService.Cancel(id);
            return RedirectToAction("List");
        }

        [HttpGet]
        public ActionResult Invoice(long id = 0)
        {
            //- Marcamos como facturado
            OrdenService.Invoice(id);

            //- Exportamos para imprimir
            PrintTicketOrden(OrdenService.Get(x => x.Id == id));

            //- Volvemos a la lista
            return RedirectToAction("List");
        }

        [HttpGet]
        public ActionResult Print(long id = 0)
        {
            //- Exportamos para imprimir
            PrintTicketOrden(OrdenService.Get(x => x.Id == id));

            //- Volvemos a la lista
            return RedirectToAction("List");
        }

        public override ActionResult List(string filter, int page = 1)
        {
            CacheManager.Clear();
            List<Orden> entities = new List<Orden>();
            long id = 0;

            if (long.TryParse(filter, out id))
                entities = OrdenService.Find(x => x.Id == id).ToList();
            else
                entities = OrdenService.Find(x => (filter == null || x.Empleado.Apellido.Contains(filter)
                                                            || x.Empleado.Nombre.Contains(filter)
                                                            || x.Mesa.Nombre.Contains(filter))
                                                            && x.Estado == EstadoOrden.Abierta)
                                             .OrderByDescending(x => x.Total)
                                             .Skip((page - 1) * PageSize)
                                             .Take(PageSize).ToList();

            OrdenViewModel model = new OrdenViewModel
            {
                Entities = entities,
                PagingInfo = new PagingInfo
                {
                    CurrentPage = page,
                    ItemsPerPage = PageSize,
                    TotalItems = OrdenService.Find(x => (filter == null || x.Empleado.Apellido.Contains(filter)
                                                                  || x.Empleado.Nombre.Contains(filter)
                                                                  || x.Mesa.Nombre.Contains(filter))
                                                                  && x.Estado == EstadoOrden.Abierta)
                                         .Count()
                }
            };
            model.Filter = filter;
            return View(model);
        }

        public override ActionResult Save(OrdenViewModel model)
        {
            List<OrdenDetalleBase> detalle = CacheManager.Get<List<OrdenDetalleBase>>("OrdenDetalleBase");
            model.Entity.Detalle = detalle;

            //- Carga combo de empleados
            if (model.Empleados == null) model.Empleados = EmpleadoService.GetAllEmpleadosAtiendeMesayActivos().OrderBy(x => x.NombreApellido);

            //- Carga combo de categorias
            if (model.Categorias == null) model.Categorias = CategoriaService.GetAllCategoriasContainsProductos().OrderBy(x => x.Nombre);

            //- Carga combo promociones
            if (model.Promociones == null) model.Promociones = PromocionService.GetAllActivas().OrderBy(x => x.Descripcion);

            //- Guarda o actualiza
            if (model.Entity.Id == 0)
            {
                if (model.Zonas == null) model.Zonas = ZonaService.GetAllZonasHabilitadasContainsMesa().OrderBy(x => x.Descripcion);
                ErrorList = OrdenService.Add(model.Entity);
            }
            else
            {
                if (model.Zonas == null) model.Zonas = ZonaService.GetAllZonasHabilitadasContainsMesasLibres().OrderBy(x => x.Descripcion);
                ErrorList = OrdenService.Update(model.Entity);
            }

            return ShowError(model, ErrorList, model.Entity.Id);
        }

        [ActionName("FillMesaEditJson")]
        public JsonResult FillMesaEdit(int id = 0)
        {
            List<Mesa> mesasList = MesaService.GetAllMesasLibresByZona(id).ToList();
            mesasList.Add(CacheManager.Get<OrdenViewModel>("OrdenViewModel").Entity.Mesa);
            var mesas = mesasList.Select(x => new { Id = x.Id, Nombre = x.Nombre })
                                    .OrderBy(x => x.Id).ToList();

            return this.Json(mesas, JsonRequestBehavior.AllowGet);
        }

        [ActionName("FillMesaJson")]
        public JsonResult FillMesa(int id = 0)
        {
            var mesas = MesaService.GetAllMesasLibresByZona(id)
                                    .Select(x => new { Id = x.Id, Nombre = x.Nombre })
                                    .OrderBy(x => x.Id);
            return this.Json(mesas, JsonRequestBehavior.AllowGet);
        }

        [ActionName("FillProductoJson")]
        public JsonResult FillProducto(int id = 0)
        {
            var productos = ProductoService.GetAllProductosByCategoria(id)
                                    .Select(x => new { Id = x.Id, Nombre = x.Nombre })
                                    .OrderBy(x => x.Nombre);
            return this.Json(productos, JsonRequestBehavior.AllowGet);

        }

        [HttpPost]
        public PartialViewResult DetalleAdd(int tipo, OrdenViewModel model)
        {
            bool actualizoCantidad = false;
            //- Cachamos items agregados
            if (!CacheManager.IsSet("OrdenDetalleBase"))
            { CacheManager.Set("OrdenDetalleBase", new List<OrdenDetalleBase>()); }

            //- Creamos nuevo item si es producto
            OrdenDetalleBase item;
            if (tipo == 1)
            {
                item = new OrdenDetalleProducto
                {
                    Cantidad = model.CantidadProducto,
                    Estado = EstadoDetalle.Pendiente,
                    FechaPedido = DateTime.Now,
                    ProductoId = model.Producto.Id,
                    Producto = ProductoService.Get(x => x.Id == model.Producto.Id)
                };

                //- Si existe producto actualizamos solo cantidad
                foreach (var detalle in CacheManager.Get<List<OrdenDetalleBase>>("OrdenDetalleBase"))
                {
                    if (detalle is OrdenDetalleProducto)
                        if (((OrdenDetalleProducto)detalle).ProductoId == model.Producto.Id)
                        {
                            ((OrdenDetalleProducto)detalle).Cantidad += model.CantidadProducto;
                            actualizoCantidad = true;
                        }
                }
                //- Agregamos producto al detalle si es no existe
                if (!actualizoCantidad) CacheManager.Get<List<OrdenDetalleBase>>("OrdenDetalleBase").Add(item);
            }

            //- Creamos nuevo item si es promocion
            if (tipo == 2)
            {
                item = new OrdenDetallePromocion
                {
                    Cantidad = model.CantidadPromocion,
                    Estado = EstadoDetalle.Pendiente,
                    FechaPedido = DateTime.Now,
                    PromocionId = model.Promocion.Id,
                    Promocion = PromocionService.Get(x => x.Id == model.Promocion.Id)
                };

                //- Si existe promocion actualizamos solo cantidad
                foreach (var detalle in CacheManager.Get<List<OrdenDetalleBase>>("OrdenDetalleBase"))
                {
                    if (detalle is OrdenDetallePromocion)
                        if (((OrdenDetallePromocion)detalle).PromocionId == model.Promocion.Id)
                        {
                            ((OrdenDetallePromocion)detalle).Cantidad += model.CantidadPromocion;
                            actualizoCantidad = true;
                        }
                }
                //- Agregamos promocion nueva al detalle si no existe
                if (!actualizoCantidad) CacheManager.Get<List<OrdenDetalleBase>>("OrdenDetalleBase").Add(item);
            }

            //- Actualizamos el detalle en cabecera y metemos en sesion al model
            model.Entity.Detalle = CacheManager.Get<List<OrdenDetalleBase>>("OrdenDetalleBase");
            CacheManager.Set("OrdenViewModel", model);

            //- Devolvemos el model solo al detalle para refrezcar grilla
            return PartialView("_OrdenDetalle", CacheManager.Get<OrdenViewModel>("OrdenViewModel"));
        }

        [HttpPost]
        public PartialViewResult DetalleRemove(Guid guid)
        {
            //- Eliminamos el Item
            CacheManager.Get<List<OrdenDetalleBase>>("OrdenDetalleBase")
                .RemoveAll(x => x.Guid == guid);

            //- Actaulizamos los datos
            CacheManager.Get<OrdenViewModel>("OrdenViewModel").Entity.Detalle
                = CacheManager.Get<List<OrdenDetalleBase>>("OrdenDetalleBase");

            //- Devolvemos el model solo al detalle para refrezcar grilla
            return PartialView("_OrdenDetalle", CacheManager.Get<OrdenViewModel>("OrdenViewModel"));
        }

        [HttpPost]
        public PartialViewResult ItemAdd(Guid guid)
        {
            //- Incrementamos la cantidad
            foreach (var item in CacheManager.Get<List<OrdenDetalleBase>>("OrdenDetalleBase"))
                if (item.Guid == guid) item.Cantidad++;

            //- Actualizamos los datos
            CacheManager.Get<OrdenViewModel>("OrdenViewModel").Entity.Detalle
                = CacheManager.Get<List<OrdenDetalleBase>>("OrdenDetalleBase");

            //- Devolvemos el model solo al detalle para refrezcar grilla
            return PartialView("_OrdenDetalle", CacheManager.Get<OrdenViewModel>("OrdenViewModel"));
        }

        [HttpPost]
        public PartialViewResult ItemRemove(Guid guid)
        {
            //- Disminuimos la cantidad
            foreach (var item in CacheManager.Get<List<OrdenDetalleBase>>("OrdenDetalleBase"))
                if (item.Guid == guid && item.Cantidad > 1) item.Cantidad--;

            //- Actaulizamos los datos
            CacheManager.Get<OrdenViewModel>("OrdenViewModel").Entity.Detalle
                = CacheManager.Get<List<OrdenDetalleBase>>("OrdenDetalleBase");

            //- Devolvemos el model solo al detalle para refrezcar grilla
            return PartialView("_OrdenDetalle", CacheManager.Get<OrdenViewModel>("OrdenViewModel"));
        }

        [HttpPost]
        public PartialViewResult PartialPayAdd(Guid guid)
        {
            //- Incrementamos la cantidad
            foreach (var item in CacheManager.Get<List<OrdenDetalleBase>>("OrdenDetalleBase"))
                if (item.Guid == guid && item.Cantidad > (item.CantidadRegalada + item.CantidadPagada)) item.CantidadPagada++;

            //- Actualizamos los datos
            CacheManager.Get<OrdenViewModel>("OrdenViewModel").Entity.Detalle
                = CacheManager.Get<List<OrdenDetalleBase>>("OrdenDetalleBase");

            //- Devolvemos el model solo al detalle para refrezcar grilla
            return PartialView("_OrdenDetalle", CacheManager.Get<OrdenViewModel>("OrdenViewModel"));
        }

        [HttpPost]
        public PartialViewResult PartialPayRemove(Guid guid)
        {
            //- Disminuimos la cantidad
            foreach (var item in CacheManager.Get<List<OrdenDetalleBase>>("OrdenDetalleBase"))
                if (item.Guid == guid && item.CantidadPagada > 0) item.CantidadPagada--;

            //- Actaulizamos los datos
            CacheManager.Get<OrdenViewModel>("OrdenViewModel").Entity.Detalle
                = CacheManager.Get<List<OrdenDetalleBase>>("OrdenDetalleBase");

            //- Devolvemos el model solo al detalle para refrezcar grilla
            return PartialView("_OrdenDetalle", CacheManager.Get<OrdenViewModel>("OrdenViewModel"));
        }

        [HttpPost]
        public PartialViewResult GiftAdd(Guid guid)
        {
            //- Incrementamos la cantidad
            foreach (var item in CacheManager.Get<List<OrdenDetalleBase>>("OrdenDetalleBase"))
                if (item.Guid == guid && item.Cantidad > (item.CantidadRegalada + item.CantidadPagada)) item.CantidadRegalada++;

            //- Actualizamos los datos
            CacheManager.Get<OrdenViewModel>("OrdenViewModel").Entity.Detalle
                = CacheManager.Get<List<OrdenDetalleBase>>("OrdenDetalleBase");

            //- Devolvemos el model solo al detalle para refrezcar grilla
            return PartialView("_OrdenDetalle", CacheManager.Get<OrdenViewModel>("OrdenViewModel"));
        }

        [HttpPost]
        public PartialViewResult GiftRemove(Guid guid)
        {
            //- Disminuimos la cantidad
            foreach (var item in CacheManager.Get<List<OrdenDetalleBase>>("OrdenDetalleBase"))
                if (item.Guid == guid && item.CantidadRegalada > 0) item.CantidadRegalada--;

            //- Actaulizamos los datos
            CacheManager.Get<OrdenViewModel>("OrdenViewModel").Entity.Detalle
                = CacheManager.Get<List<OrdenDetalleBase>>("OrdenDetalleBase");

            //- Devolvemos el model solo al detalle para refrezcar grilla
            return PartialView("_OrdenDetalle", CacheManager.Get<OrdenViewModel>("OrdenViewModel"));
        }

        //public ActionResult Report(string id)
        //{
        //    LocalReport localReport = new LocalReport();
        //    localReport.ReportPath = Path.Combine(Server.MapPath("~/Reports"), "Orden.rdlc");

        //    List<Orden> cm = new List<Orden>();

        //    ReportDataSource rd = new ReportDataSource("MyDataset", cm);
        //    localReport.DataSources.Add(rd);
        //    string reportType = id;
        //    string mimeType;
        //    string encoding;
        //    string fileNameExtension;
        //    string deviceInfo =

        //    "<DeviceInfo>" +
        //    "  <OutputFormat>" + id + "</OutputFormat>" +
        //    "  <PageWidth>8.5in</PageWidth>" +
        //    "  <PageHeight>11in</PageHeight>" +
        //    "  <MarginTop>0.5in</MarginTop>" +
        //    "  <MarginLeft>1in</MarginLeft>" +
        //    "  <MarginRight>1in</MarginRight>" +
        //    "  <MarginBottom>0.5in</MarginBottom>" +
        //    "</DeviceInfo>";

        //    Warning[] warnings;
        //    string[] streams;
        //    byte[] renderedBytes;

        //    renderedBytes = localReport.Render(
        //        reportType,
        //        deviceInfo,
        //        out mimeType,
        //        out encoding,
        //        out fileNameExtension,
        //        out streams,
        //        out warnings);

        //    return File(renderedBytes, mimeType, "Orden");
        //}

        private bool PrintTicketOrden(Orden orden)
        {
            try
            {
                ContractClient svc = new ContractClient();
                TicketOrder ticketOrder = new TicketOrder();                

                #region Cabecera
                ticketOrder.OrdenId = orden.Id;
                ticketOrder.Zona = orden.Mesa.Zona.Descripcion;
                ticketOrder.Mesa = orden.Mesa.Nombre;
                ticketOrder.Mozo = orden.Empleado.Nombre;
                ticketOrder.Total = orden.Total.Value;
                ticketOrder.TotalPorPagar = orden.TotalPorPagar.Value;
                ticketOrder.TotalPagado = orden.TotalPagado.Value;
                ticketOrder.TotalRegalado = orden.TotalRegalado.Value;
                #endregion

                #region Detalle
                ticketOrder.Detalle = new List<Item>();
                foreach (var item in orden.Detalle)
                {
                    if (item is OrdenDetalleProducto)
                    {
                        ticketOrder.Detalle.Add(new Item
                        {
                            Cantidad = item.Cantidad,
                            CantidadPagado = item.CantidadPagada,
                            CantidadRegalo = item.CantidadRegalada,
                            CantidadPorPagar = item.CantidadPorPagar,
                            Descripcion = (item as OrdenDetalleProducto).Producto.Nombre,
                            PrecioUnitario = item.Precio,
                            SubTotal = item.SubTotal,
                            SubTotalPagado = item.SubTotalPagado,
                            SubTotalPorPagar = item.SubTotalPorPagar,
                            SubTotalRegalado = item.SubTotalRegalos
                        });
                    }

                    if (item is OrdenDetallePromocion)
                    {
                        ticketOrder.Detalle.Add(new Item
                        {
                            Cantidad = item.Cantidad,
                            CantidadPagado = item.CantidadPagada,
                            CantidadRegalo = item.CantidadRegalada,
                            CantidadPorPagar = item.CantidadPorPagar,
                            Descripcion = (item as OrdenDetallePromocion).Promocion.Descripcion,
                            PrecioUnitario = item.Precio,
                            SubTotal = item.SubTotal,
                            SubTotalPagado = item.SubTotalPagado,
                            SubTotalPorPagar = item.SubTotalPorPagar,
                            SubTotalRegalado = item.SubTotalRegalos
                        });
                    }
                }
                #endregion

                PrintOrderRequest request = new PrintOrderRequest();
                request.ticketOrder = ticketOrder;
                svc.PrintOrder(request);
                return true;
            }
            catch (Exception ex)
            { return false; }
        }
    }
}